/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package midi;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import  javax.sound.midi.*;
import music.Note;
import music.types.MidiNote;
/**
 *
 * @author TJ
 */
public class MidiPlayer {
    private Synthesizer synth;
    private Sequencer seq;
    private Sequence record, file;
    private MidiChannel[] m;
    private NotePlayerThread t;
    private static MidiPlayer m2;
    private static Exception loadException=null;
    private MidiPlayer() throws MidiUnavailableException, InvalidMidiDataException, IOException {
        synth=MidiSystem.getSynthesizer();
        
        synth.loadAllInstruments(synth.getDefaultSoundbank());
        synth.open();
        //seq=MidiSystem.getSequencer();
        //seq.open();
        m=synth.getChannels();
    }
    public static MidiPlayer getMidiPlayer(){
        try{
            if(m2==null){m2=new MidiPlayer();}
        
        } catch(Exception e){loadException=e;}
        return m2;
    }
    public static Exception getLoadException(){
        return loadException;
    }
    public Instrument[] getInstrumentList() {
        return synth.getAvailableInstruments();
    }
    public void record(){}
    public void stopRecord(){}
    public void setInstrument(int channel, int instrument) {
        m[channel].programChange(instrument);
    }
    public void mute(int channel){
        //m[channel];
    }
    public void stopAllNotes(int channel){
        m[channel].allNotesOff();
    }
    public void playNote(int channel,int note,int volume) {
        m[channel].noteOn(note, volume);
    }
    public void stopNote(int channel,int note) {
        m[channel].noteOff(note);
    }
    public void playNote(Note n,long length){
        NotePlayerThread tmp=new NotePlayerThread(length,n.getNotes());
        if(t!=null && tmp.getChannel()==t.getChannel()) {t.kill();}
        Thread t2=new Thread(tmp);
        t2.start();
        t=tmp;
    }

    public void changeInstrument(int channel, int newProgram) {
        synth.getChannels()[channel].programChange(newProgram);
    }
    class NotePlayerThread implements Runnable{
        long len;
        Vector<MidiNote> notesToPlay;
        boolean kill=false;
        int instrument=1,channel=1,volume=120;
        NotePlayerThread(long length,Vector<MidiNote> play) {len=length;notesToPlay=play;}
        NotePlayerThread(long length) {len=length;notesToPlay=new Vector<MidiNote>();}
        public void setNotesToPlay(Vector<MidiNote> notes) {notesToPlay=notes;}
        public void setInstrument(int i){instrument=i;}
        public void setChannel(int i){channel=i;}
        public int getChannel(){return channel;}
        public void setVolume(int b) {
            volume=b;
            if(volume<0) {volume=0;}
            if(volume>127){volume=127;}
        }
        public void kill(){kill=true;}
        public void run() {
            MidiPlayer.this.setInstrument(channel, instrument);
            try{
                MidiPlayer.this.stopAllNotes(channel);
                for(MidiNote i : notesToPlay){
                MidiPlayer.this.playNote(channel, i.getMidi(), volume);
                }
                Thread.sleep(len);
                if(kill){return;}
                for(MidiNote i : notesToPlay){
                MidiPlayer.this.stopNote(channel, i.getMidi());
                }
            }catch(Exception e){}
        }
        
    }
}
